package com.xingda.packing.dmds.minitor.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.eventbus.EventBus;
import com.xingda.packing.dmds.common.DateUtils;
import com.xingda.packing.dmds.common.PackUtils;
import com.xingda.packing.dmds.config.appset.AppSetParam;
import com.xingda.packing.dmds.domain.CacheLocation;
import com.xingda.packing.dmds.domain.PackProductionLine;
import com.xingda.packing.dmds.domain.Wheel;
import com.xingda.packing.dmds.enums.*;
import com.xingda.packing.dmds.events.EventCreater;
import com.xingda.packing.dmds.events.dto.*;
import com.xingda.packing.dmds.minitor.entity.*;
import com.xingda.packing.dmds.minitor.mapper.*;
import com.xingda.packing.dmds.minitor.service.IPkActionService;
import com.xingda.packing.dmds.minitor.service.IPkTaskService;
import com.xingda.packing.dmds.minitor.service.IPkTaskUploadService;
import com.xingda.packing.dmds.task.ticket.CachePackingTicket;
import com.xingda.packing.dmds.task.ticket.PackingTicket;
import com.xingda.packing.dmds.task.ticket.ScanCodeTicket;
import com.xingda.packing.dmds.task.ticket.StorageTicket;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 */
@Slf4j
@Service("pkActionDMDSService")
public class PkActionServiceImpl extends ServiceImpl<PkActionMapper, PkAction> implements IPkActionService {

    @Resource
    PkWheelMapper pkWheelMapper;

    @Resource
    PkTaskInfoMapper pkTaskInfoMapper;

    @Resource
    PkCacheLocationMapper pkCacheLocationMapper;

    @Resource
    IPkTaskService pkTaskService;

    @Resource
    EventBus eventBus;

    @Resource
    PkActionMapper pkActionMapper;

    @Resource
    PackProductionLine packProductionLine;

    @Resource
    EventCreater eventCreater;

    @Resource
    IPkTaskUploadService pkTaskUploadService;

    @Resource
    PkDownWheelMapper pkDownWheelMapper;
    @Value("${com.xingda.pkRobotdoc}")
    private String pkRobotdoc;

    @Override
    public void addPkAction(EventActionScanCode actionScanCode) {
        LambdaQueryWrapper<PkAction> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(PkAction::getActionType, ActionType.SCAN_CODE.getNumber());
        lambdaQueryWrapper.eq(PkAction::getArea, actionScanCode.getArea());
        lambdaQueryWrapper.isNull(PkAction::getFinishTime);
        PkAction action = pkActionMapper.selectOne(lambdaQueryWrapper);
        if (Objects.nonNull(action)) {
            removeById(action.getId());
        }
        ScanCodeTicket ticket = actionScanCode.getTicket();
        PkAction pkAction = new PkAction();
        pkAction.setStartTime(new Date());
        pkAction.setActionType(ActionType.SCAN_CODE.getNumber());
        pkAction.setFinishState(ActionFinishState.START.getNumber());
        pkAction.setToLocation(ticket.getLocationNo());
        pkAction.setFromLocation(0);
        pkAction.setArea(actionScanCode.getArea());
        this.save(pkAction);
        log.info("保存到抓取位{}动作命令:{},{}", ticket.getLocationNo(), pkAction.getWheelId(), pkAction.getId());
    }

    @Transactional
    @Override
    public void addPkAction(EventActionPacking actionPacking) {
        PackingTicket ticket = actionPacking.getTicket();
        Wheel wheel = ticket.getWheel();
        PkWheel pkWheel = PackUtils.toPkWheel(wheel);
        pkWheelMapper.insert(pkWheel);
        int pkWheelId = pkWheel.getId();
        PkAction pkAction = new PkAction();
        pkAction.setStartTime(new Date());
        pkAction.setActionType(ActionType.PACKING.getNumber());
        // 完成状态1开始2完成
        pkAction.setFinishState(ActionFinishState.START.getNumber());
        pkAction.setToLocation(ticket.getTag());
        pkAction.setWheelId(pkWheelId);
        pkAction.setFromLocation(100);
        pkAction.setArea(actionPacking.getArea());
        this.save(pkAction);
        log.info("保存从抓取位到装箱位{}动作的命令:{},{}", ticket.getProductBoxLocationNo(), pkAction.getWheelId(), pkAction.getId());
    }

    @Override
    public void addPkAction(EventActionStorage actionStorage) {
        StorageTicket ticket = actionStorage.getTicket();
        Wheel wheel = ticket.getWheel();
        PkWheel pkWheel = PackUtils.toPkWheel(wheel);
        pkWheelMapper.insert(pkWheel);
        int pkWheelId = pkWheel.getId();
        PkAction pkAction = new PkAction();
        pkAction.setStartTime(new Date());
        pkAction.setActionType(ActionType.STORAGE.getNumber());
        // 完成状态1开始2完成
        pkAction.setFinishState(ActionFinishState.START.getNumber());
        pkAction.setFromLocation(100);
        pkAction.setToLocation(ticket.getCacheLocationNo());
        pkAction.setWheelId(pkWheelId);
        pkAction.setArea(actionStorage.getArea());
        this.save(pkAction);
        log.info("保存从抓取位到缓存位{}动作的命令:{},{}", ticket.getCacheLocationNo(), pkAction.getWheelId(), pkAction.getId());
    }

    @Override
    public void addPkAction(EventActionCachePacking actionCachePacking) {
        CachePackingTicket ticket = actionCachePacking.getTicket();
        Integer locationNo = ticket.getCacheLocationNo();
        int boxLocationNo = ticket.getTag();
        int area = ticket.getArea();
        LambdaQueryWrapper<PkCacheLocation> pkCacheLocationQueryWrapper = Wrappers.lambdaQuery();
        pkCacheLocationQueryWrapper.eq(PkCacheLocation::getLocationNo, locationNo);
        pkCacheLocationQueryWrapper.eq(PkCacheLocation::getArea, area);
        PkCacheLocation pkCacheLocation = pkCacheLocationMapper.selectOne(pkCacheLocationQueryWrapper);
        if (pkCacheLocation == null) {
            log.debug("根据编号{} 未找到缓存位", locationNo);
            return;
        }
        PkAction pkAction = new PkAction();
        pkAction.setStartTime(new Date());
        pkAction.setActionType(ActionType.CACHE_PALLETIZING.getNumber());
        // 完成状态1开始2完成
        pkAction.setFinishState(ActionFinishState.START.getNumber());
        pkAction.setFromLocation(ticket.getCacheLocationNo());
        pkAction.setToLocation(boxLocationNo);
        pkAction.setWheelId(pkCacheLocation.getWheelId());
        pkAction.setArea(actionCachePacking.getArea());
        this.save(pkAction);
        log.info("保存从缓存位{}到装箱位{}动作的命令:{},{}", locationNo, boxLocationNo, pkAction.getWheelId(), pkAction.getId());

    }

    @Override
    @Transactional
    public void finishPkAction(EventActionType eventActionType) {
        ActionType actionType = eventActionType.getActionType();
        LambdaQueryWrapper<PkAction> pkActionLambdaQueryWrapper = Wrappers.lambdaQuery();
        pkActionLambdaQueryWrapper.eq(PkAction::getFinishState, ActionFinishState.START.getNumber())
                .eq(PkAction::getActionType, actionType.getNumber()).orderByDesc(PkAction::getStartTime)
                .eq(PkAction::getArea, eventActionType.getArea());
        PkAction pkAction = pkActionMapper.selectOne(pkActionLambdaQueryWrapper);
        if (Objects.isNull(pkAction)) {
            log.debug("未找到该任务类型{}未结束动作！", actionType.getDesc());
            return;
        }
        finish(pkAction);
        pkAction.setFinishType(ActionFinishType.AUTO.getNumber());
        pkAction.setFinishTime(new Date());
        pkAction.setFinishState(ActionFinishState.FINISH.getNumber());
        pkAction.setArea(eventActionType.getArea());
        updateById(pkAction);
    }

    @Override
    public void finishPkAction(ActionType actionType, int area) {
        LambdaQueryWrapper<PkAction> pkActionLambdaQueryWrapper = Wrappers.lambdaQuery();
        pkActionLambdaQueryWrapper.eq(PkAction::getFinishState, ActionFinishState.START.getNumber())
                .eq(PkAction::getActionType, actionType.getNumber()).orderByDesc(PkAction::getStartTime)
                .eq(PkAction::getArea, area);
        PkAction pkAction = pkActionMapper.selectOne(pkActionLambdaQueryWrapper);
        if (Objects.isNull(pkAction)) {
            log.debug("未找到该任务类型{}未结束动作！", actionType.getDesc());
            return;
        }
        finish(pkAction);
        pkAction.setFinishType(ActionFinishType.AUTO.getNumber());
        pkAction.setFinishTime(new Date());
        pkAction.setFinishState(ActionFinishState.FINISH.getNumber());
        pkAction.setArea(area);
        updateById(pkAction);
        log.info("完成动作事件:{},{},{}", pkAction.getId(), pkAction.getWheelId(), pkAction.getActionType());
    }


    private void finish(PkAction pkAction) {
        log.info("执行放轮任务:{}, {}, {}", pkAction.getId(), pkAction.getWheelId(), pkAction.getActionType());
        if (pkAction.getActionType() == ActionType.PACKING.getNumber()) {
            packing(pkAction);
            removeDownWheel(pkAction.getWheelId());
        } else if (pkAction.getActionType() == ActionType.STORAGE.getNumber()) {
            storage(pkAction);
        } else if (pkAction.getActionType() == ActionType.CACHE_PALLETIZING.getNumber()) {
            cachePacking(pkAction);
            removeDownWheel(pkAction.getWheelId());
        }
    }

    /**
     * 移除卸轮记录
     */
    private void removeDownWheel(int wheelId) {
        PkWheel pkWheel = pkWheelMapper.selectById(wheelId);
        LambdaQueryWrapper<PkDownWheel> pkDownWheeLambdaQueryWrapper = Wrappers.lambdaQuery();
        pkDownWheeLambdaQueryWrapper.eq(PkDownWheel::getBarCode, pkWheel.getBarCode());
        pkDownWheelMapper.delete(pkDownWheeLambdaQueryWrapper);
    }

    private void packing(PkAction pkAction) {
        int boxLocationNo = pkAction.getToLocation();
        PkTask pkTask = pkTaskService.getByBoxLocationNo(boxLocationNo);
        if (pkTask == null) {
            log.error("未查询到执行中任务！");
            return;
        }
        LambdaQueryWrapper<PkTaskInfo> pktaskInfoLambdaQueryWrapper = Wrappers.lambdaQuery();
        pktaskInfoLambdaQueryWrapper.eq(PkTaskInfo::getTaskId, pkTask.getId()).orderByAsc(PkTaskInfo::getPutTime);
        List<PkTaskInfo> pkTaskInfoList = pkTaskInfoMapper.selectList(pktaskInfoLambdaQueryWrapper);
        // 每层要求装箱数量
        int floorLocationCount = pkTask.getFloorCount();
        // 当前装箱个数
        int currentPackFloorCount = pkTaskInfoList == null || pkTaskInfoList.isEmpty() ? 0 : pkTaskInfoList.size();
        currentPackFloorCount++;
        int layer = currentPackFloorCount / floorLocationCount;
        // 当前层个数
        int currentFloorCount = currentPackFloorCount % floorLocationCount;
        if (currentFloorCount > 0) {
            layer++;
        }
        Date date = new Date();
        PkTaskInfo pkTaskInfo = new PkTaskInfo();
        pkTaskInfo.setTaskId(pkTask.getId());
        pkTaskInfo.setWheelId(pkAction.getWheelId());
        pkTaskInfo.setLayer(layer);
        pkTaskInfo.setPutTime(date);
        pkTaskInfo.setPostion(currentFloorCount);
        pkTaskInfoMapper.insert(pkTaskInfo);
        // 再查下一次
        pkTaskInfoList = pkTaskInfoMapper.selectList(pktaskInfoLambdaQueryWrapper);
        // 整层码完，上传信息中心数据
        if (currentPackFloorCount % floorLocationCount == 0) {
            boolean isUpload = AppSetParam.is_floor_upload && (pkTask.getType() == BoxTaskType.PACKING_FINISHED.getTypeId()) && pkTask.isUpload();
            log.info("装箱位{}第{}层码垛完成;是否上传本层信息:{}", BoxLocationNo.getById(boxLocationNo).getNo(), layer, isUpload);
            eventCreater.createPkEvent(EventType.UPLOAD, pkAction.getArea(), "装箱位：", BoxLocationNo.getById(boxLocationNo).getNo(), "，装箱号：", pkTask.getBoxNo(), "，第", layer, "层码垛完成；<br>是否上传本层信息：", isUpload);
            //创建上传参数
            List<PkTaskInfo> subPkTaskInfos = pkTaskInfoList.subList(currentPackFloorCount - floorLocationCount, currentPackFloorCount);
            List<Integer> wheelIdList = subPkTaskInfos.stream().map(PkTaskInfo::getWheelId).collect(Collectors.toList());
            List<PkWheel> pkWheelList = pkWheelMapper.selectBatchIds(wheelIdList);
            //修改轮子顺序按放置顺序上传(从里到外1-12)
            List<Integer> sortList = AppSetParam.wheel_sort_index;
            int subSize = !CollectionUtils.isEmpty(subPkTaskInfos) ? subPkTaskInfos.size() : 0;
            //排序后集合
            List<PkWheel> pkWheelSortList = new ArrayList<>();
            //先排序后集合填充null
            pkWheelList.forEach(x -> pkWheelSortList.add(null));
            for (int j = 0; j < subPkTaskInfos.size(); j++) {
                PkTaskInfo thisPkTaskInfo = subSize - 1 >= j ? subPkTaskInfos.get(j) : null;
                if (Objects.isNull(thisPkTaskInfo)) {
                    log.info("装箱层详细信息获取出错！");
                    return;
                }
                Optional<PkWheel> pkWheelOpt = pkWheelList.stream().filter(thisPkWheel -> thisPkWheel.getId() == thisPkTaskInfo.getWheelId()).findFirst();
                if (!pkWheelOpt.isPresent()) {
                    log.info("装箱层详细信息获取出错！-工字轮id-{}未找到信息！", thisPkTaskInfo.getWheelId());
                    return;
                }
                PkWheel thisPkWheel = pkWheelOpt.get();
                int sortIndex = sortList.get(j);
                pkWheelSortList.set(sortIndex, thisPkWheel);
            }
            PackageFloorInfoParam param = createPackageFloorInfoParam(layer, pkTask.getPkCalbody(), pkTask.getPkCorp(), pkTask.getPkDeptDoc(), pkTask.getPkPacking(), pkWheelSortList);
            if (isUpload) {
                EventPackageFloorInfo eventPackageFloorInfo = new EventPackageFloorInfo();
                eventPackageFloorInfo.setBoxLocationNo(boxLocationNo);
                eventPackageFloorInfo.setLayer(layer);
                eventPackageFloorInfo.setParam(param);
                eventPackageFloorInfo.setPkTask(pkTask);
                eventPackageFloorInfo.setArea(pkAction.getArea());
                eventBus.post(eventPackageFloorInfo);
            } else {
                pkTaskUploadService.saveOrUpdate(pkTask.getId(), layer, TaskUploadState.NOT_UPLOAD.getNumber(), JSONObject.toJSONString(param), TaskUploadType.AUTO.getNumber(), TaskUploadState.NOT_UPLOAD.getDesc());
            }
        }
        // 整箱装满,更新任务状态
        if (pkTaskInfoList.size() == pkTask.getBoxWheelSum()) {
            log.info("装箱位{},第{}层码垛完成,更新整箱状态！", boxLocationNo, layer);
            eventCreater.createPkEvent(EventType.UPLOAD, pkAction.getArea(), "装箱位", boxLocationNo, "第", layer, "层码垛完成,更新整箱状态！");
            EventTaskFinishe ef = new EventTaskFinishe();
            ef.setBoxLocationNo(boxLocationNo);
            eventBus.post(ef);
        }
    }


    /**
     * 封装上传参数
     */
    private PackageFloorInfoParam createPackageFloorInfoParam(int layer, String pkCalbody, String pkCorp, String pkDeptDoc, String pkPacking, List<PkWheel> pkWheelList) {
        PackageFloorInfoParam packageFloorInfoParam = new PackageFloorInfoParam();
        packageFloorInfoParam.setLayer(layer);
        packageFloorInfoParam.setPacktime(DateUtils.getNowDate());
        packageFloorInfoParam.setPkCalbody(pkCalbody);
        packageFloorInfoParam.setPkCorp(pkCorp);
        packageFloorInfoParam.setPkDeptdoc(pkDeptDoc);
        packageFloorInfoParam.setPkPacking(pkPacking);
        List<ItemPositionParam> items = new ArrayList<>();
        for (int i = 0; i < pkWheelList.size(); i++) {
            PkWheel pkWheel = pkWheelList.get(i);
            ItemPositionParam itemPositionParam = new ItemPositionParam();
            itemPositionParam.setPkIntermediate(pkWheel.getWheelPk());
            itemPositionParam.setPosition(i + 1);
            itemPositionParam.setHwheelbarcode(pkWheel.getBarCode());
            items.add(itemPositionParam);
        }
        packageFloorInfoParam.setPkRobotdoc(pkRobotdoc);
        packageFloorInfoParam.setItems(items);
        packageFloorInfoParam.setSolderjointcount(pkWheelList.stream().map(PkWheel::getSolder).reduce(0, Integer::sum));
        packageFloorInfoParam.setTorsionsum(pkWheelList.stream().map(PkWheel::getTorqueValue).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add));
        return packageFloorInfoParam;
    }

    private void storage(PkAction pkAction) {
        int cacheLocationId = pkAction.getToLocation();
        LambdaQueryWrapper<PkCacheLocation> lambdaQueryWrapper = Wrappers.lambdaQuery();
        lambdaQueryWrapper.eq(PkCacheLocation::getLocationNo, cacheLocationId);
        lambdaQueryWrapper.eq(PkCacheLocation::getArea, pkAction.getArea());
        List<PkCacheLocation> pkCacheLocations = pkCacheLocationMapper.selectList(lambdaQueryWrapper);
        if (pkCacheLocations == null || pkCacheLocations.isEmpty()) {
            log.error("未查询到缓存位置号{}信息！", cacheLocationId);
            return;
        }
        PkCacheLocation pkCacheLocation = pkCacheLocations.get(0);
        if (pkCacheLocation.getWheelId() != null) {
            log.warn("缓存位置号{},存在工字轮！", cacheLocationId);
            return;
        }
        pkCacheLocation.setWheelId(pkAction.getWheelId());
        pkCacheLocationMapper.updateById(pkCacheLocation);
    }

    private void cachePacking(PkAction pkAction) {
        //查询缓存位信息
        LambdaQueryWrapper<PkCacheLocation> pkCacheLocationQueryWrapper = Wrappers.lambdaQuery();
        pkCacheLocationQueryWrapper.eq(PkCacheLocation::getLocationNo, pkAction.getFromLocation())
                .eq(PkCacheLocation::getArea, pkAction.getArea());
        PkCacheLocation pkCacheLocation = pkCacheLocationMapper.selectOne(pkCacheLocationQueryWrapper);
        if (Objects.isNull(pkCacheLocation)) {
            log.error("未在数据库中查询到-此缓存编号{}", pkAction.getFromLocation());
            return;
        }
        //更新缓存位为null
        UpdateWrapper<PkCacheLocation> updateWrapper = new UpdateWrapper<>();
        updateWrapper.set("wheel_id", null);
        updateWrapper.eq("id", pkCacheLocation.getId());
        pkCacheLocationMapper.update(pkCacheLocation, updateWrapper);
        packProductionLine.getCacheArea().getLocationList(pkAction.getArea()).clear();
        packProductionLine.getCacheArea().initStatus(pkAction.getArea());
        packing(pkAction);
    }

    @Override
    public IPage<PkAction> getList(Integer actionType, Integer finishState, Integer area, Long current, Long size) {
        LambdaQueryWrapper<PkAction> pkActionQueryWrapper = Wrappers.lambdaQuery();
        pkActionQueryWrapper.eq(actionType != null && actionType != -1, PkAction::getActionType, actionType)
                .eq(finishState != null && finishState != -1, PkAction::getFinishState, finishState)
                .eq(area != null && area != -1, PkAction::getArea, area)
                .orderByDesc(PkAction::getStartTime);
        current = current == null ? 1 : current;
        size = size == null ? 20 : size;
        IPage<PkAction> page = new Page<>(current, size, true);
        return pkActionMapper.selectPage(page, pkActionQueryWrapper);
    }

    @Transactional
    @Override
    public void modifyById(Integer id, Integer finishState) {
        PkAction pkAction = pkActionMapper.selectById(id);
        // 手动完成
        if (finishState == ActionFinishState.FINISH.getNumber()) {
            finish(pkAction);
            PkWheel pkWheel = pkWheelMapper.selectById(pkAction.getWheelId());
            Wheel wheel = PackUtils.toWheel(pkWheel);
            int area = pkAction.getArea();
            // 动作 1扫码 2直接码垛 3存放缓存位 4从缓存位码垛
            if (pkAction.getActionType() == ActionType.PACKING.getNumber()) {
                // 判断机器人抓取的轮子和要完成的轮子是否是同一个
                if (packProductionLine.getRobot().haveWheel(area)
                        && packProductionLine.getRobot().getWheel(area).getBarCode().equalsIgnoreCase(wheel.getBarCode())) {
                    packProductionLine.getRobot().clear(area);
                }
                packProductionLine.getProductArea().getProductBox(pkAction.getToLocation()).get().putWheel(wheel);
            } else if (pkAction.getActionType() == ActionType.STORAGE.getNumber()) {
                // 判断机器人抓取的轮子和要完成的轮子是否是同一个
                if (packProductionLine.getRobot().haveWheel(area)
                        && packProductionLine.getRobot().getWheel(area).getBarCode().equalsIgnoreCase(wheel.getBarCode())) {
                    packProductionLine.getRobot().clear(area);
                }
                packProductionLine.getCacheArea().putWheel(pkAction.getToLocation(), wheel, LocalDateTime.now(), area);
            } else if (pkAction.getActionType() == ActionType.CACHE_PALLETIZING.getNumber()) {
                Optional<CacheLocation> opt = packProductionLine.getCacheArea().getLocation(pkAction.getFromLocation(), area);
                if (opt.isPresent()) {
                    Wheel cacheWheel = opt.get().getWheel();
                    if (Objects.nonNull(cacheWheel) && packProductionLine.getRobot().getWheel(area).getBarCode()
                            .equalsIgnoreCase(cacheWheel.getBarCode())) {
                        packProductionLine.getCacheArea().takeOutWheel(pkAction.getFromLocation(), area);
                    }
                }
                packProductionLine.getProductArea().getProductBox(pkAction.getToLocation()).get().putWheel(wheel);
            }
        }
        pkAction.setFinishState(finishState);
        pkAction.setFinishTime(new Date());
        pkAction.setFinishType(ActionFinishType.MANUAL.getNumber());
        updateById(pkAction);
    }

}
